package cn.rtplay.esp32camcar.SteeringWheelSeekBar;

import static java.lang.Math.atan2;
import static java.lang.Math.sqrt;
import static java.lang.Math.toDegrees;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.util.Pair;
import android.view.MotionEvent;
import android.view.ViewGroup;
import android.widget.FrameLayout;

import cn.rtplay.esp32camcar.R;
import cn.rtplay.esp32camcar.toolUtils.LogUtil;

/**
 * 自定义方向盘SeekBar ViewGroup
 */
public class SteeringWheelSeekBar extends FrameLayout {
    private final String TAG = "SteeringWheelSeekBar";
    private float currentDegrees = 0;                  // 当前角度
    private float lastDegrees = 0;                  // 最后角度，用于经过档位去重判断
    private static final int LEFT_LIMIT_ANGLE = 80;
    private static final int RIGHT_LIMIT_ANGLE = -80;

    private SteeringWheelSeekBar.OnProgressChangedListener listener; // 角度改变的监听
    private Context context;

    private SeekBarForegroundView foregroundView;   // 前景view
    private int foregroundSize;                 // 图片的大小
    private int foregroundId;                   // 前景图片id
    private float absoluteDegree = 0;           // 绝对旋转角度
    private int clockwiseLimitAngle = Integer.MAX_VALUE; // 逆时针角度
    private int counterclockwiseLimitAngle = Integer.MIN_VALUE; // 顺时针角度
    PointF centerPoint;                         // 中心点坐标
    PointF pressPoint;                          // 按下点坐标
    PointF movePoint;                           // 移动点坐标


    public SteeringWheelSeekBar(Context context) {
        super(context);
        init();
    }

    public SteeringWheelSeekBar(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;
        TypedArray array =
                context.obtainStyledAttributes(attrs, R.styleable.SteeringWheelSeekBar);
        foregroundSize =
                array.getDimensionPixelSize(R.styleable.SteeringWheelSeekBar_foregroundSize, 200);
        foregroundId = array.getResourceId(R.styleable.SteeringWheelSeekBar_foreground, 0);
        clockwiseLimitAngle = array.getInt(R.styleable.SteeringWheelSeekBar_clockwiseLimitAngle, Integer.MAX_VALUE);
        counterclockwiseLimitAngle = array.getInt(R.styleable.SteeringWheelSeekBar_counterclockwiseLimitAngle, Integer.MIN_VALUE);
        array.recycle();
        init();
    }

    private void init() {
        centerPoint = new PointF();
        pressPoint = new PointF();
        movePoint = new PointF();

        // 背景view
        SeekBarBackgroundView backgroundView = new SeekBarBackgroundView(context);
        backgroundView.setLayoutParams(new LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.MATCH_PARENT));
        addView(backgroundView);

        foregroundView = new SeekBarForegroundView(context, foregroundSize, foregroundId);
        addView(foregroundView);
    }

    public void setForeground(int foregroundId) {
        removeView(foregroundView);
        foregroundView = new SeekBarForegroundView(context, foregroundSize, foregroundId);
        addView(foregroundView);
    }

    // 设置最终档位监听
    public void setListener(SteeringWheelSeekBar.OnProgressChangedListener listener) {
        this.listener = listener;
    }

    // 设置旋转限制角度
    public SteeringWheelSeekBar setLimitAngle(int clockwise, int counterclockwise) {
        clockwiseLimitAngle = clockwise;
        counterclockwiseLimitAngle = counterclockwise;
        return this;
    }

    @Override
    protected void onLayout(boolean changed,
                            int left, int top,
                            int right,
                            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        centerPoint.set((float) (right - left) / 2, (float) (bottom - top) / 2);
        // 放置在父view的中间
        changeBallLayout((right - left) / 2, (bottom - top) / 2);
    }

    @Override
    public boolean performClick() {
        return super.performClick();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                pressPoint = getRelativelyCoordinate(new PointF(event.getX(), event.getY()));
                return true;
            case MotionEvent.ACTION_MOVE:
                movePoint = getRelativelyCoordinate(new PointF(event.getX(), event.getY()));
                // 获取与按下点的相对夹角
                float relativelyDegree = getRotationDegrees();
                // 当前角度
                currentDegrees = absoluteDegree + relativelyDegree;

                if (lastDegrees != currentDegrees) {
                    /*此处的300是对每次转角差值的一个大致范围定义，这个差值大小与系统刷新率有关，刷新率越快差值越小，我用模拟器测试差值最大是340左右
                    这里用300取商，对于多圈后误差会更大，理论上4圈以后就会出错，因为4圈后会计算为第5圈，因为每次除法都会有60度超出*/
//                    LogUtil.e(TAG, "lastDegrees: " + lastDegrees + " currentDegrees: " + currentDegrees);
                    currentDegrees += 360 * (int)((lastDegrees - currentDegrees) / 300);
                    if (currentDegrees <= (clockwiseLimitAngle + 0.4f) && currentDegrees >= (counterclockwiseLimitAngle - 0.4f)) {
                        // 设置旋转角度
//                        LogUtil.e(TAG, "currentDegrees: " + currentDegrees);
                        foregroundView.setDegrees(currentDegrees);
                        listener.OnProgressChanged((int) currentDegrees);
                    } else if (currentDegrees > clockwiseLimitAngle) {
                        if (currentDegrees < lastDegrees) { // 当手势往回转时直接清空累计的转角
                            // 模拟手势抬起后又按下了的动作
                            currentDegrees = clockwiseLimitAngle;
                            lastDegrees = currentDegrees;
                            absoluteDegree = lastDegrees;
                            pressPoint = getRelativelyCoordinate(new PointF(event.getX(), event.getY()));
                        }
                    } else if (currentDegrees < counterclockwiseLimitAngle) {
                        if (currentDegrees > lastDegrees) { // 当手势往回转时直接清空累计的转角
                            // 模拟手势抬起后又按下了的动作
                            currentDegrees = counterclockwiseLimitAngle;
                            lastDegrees = currentDegrees;
                            absoluteDegree = lastDegrees;
                            pressPoint = getRelativelyCoordinate(new PointF(event.getX(), event.getY()));
                        }
                    }
                    lastDegrees = currentDegrees;
//                    LogUtil.e(TAG, "==lastDegrees: " + lastDegrees + " currentDegrees: " + currentDegrees);
                }
                break;
            case MotionEvent.ACTION_UP:
                movePoint = getRelativelyCoordinate(new PointF(event.getX(), event.getY()));

                if (currentDegrees > clockwiseLimitAngle) {
                    lastDegrees = clockwiseLimitAngle;
                } else if (currentDegrees < counterclockwiseLimitAngle) {
                    lastDegrees = counterclockwiseLimitAngle;
                }

                absoluteDegree = lastDegrees;
                performClick();
                break;
            default:
                // 当手指移出View时，目前好像不会进入到这个case中
                LogUtil.d(TAG, "onTouchEvent default: " + event.getX() + "," + event.getY());
                movePoint.set(event.getX(), event.getY());
                absoluteDegree += getRotationDegrees();
                if (listener != null) {
                    listener.OnProgressChanged((int) absoluteDegree);
                }
                break;
        }
        return super.onTouchEvent(event);
    }

    /**
     * 改变球的位置，将球在参数坐标处描画出来
     *
     * @param currentX 当前x坐标
     * @param currentY 当前Y坐标
     */
    private void changeBallLayout(int currentX, int currentY) {
        foregroundView.layout((int) ((float) currentX - (float) foregroundView.getMeasuredWidth() / 2),
                (int) ((float) currentY - (float) foregroundView.getMeasuredWidth() / 2),
                (int) ((float) currentX + (float) foregroundView.getMeasuredWidth() / 2),
                (int) ((float) currentY + (float) foregroundView.getMeasuredWidth() / 2));
    }

    /**
     * 滑动接口
     */
    public interface OnProgressChangedListener {
        void OnProgressChanged(int level);
    }

    /**
     * @return 返回相对于按下点的旋转角度
     */
    private float getRotationDegrees() {
        Pair<Float, Float> pressPolar = getPolarCoordinate(pressPoint.x, pressPoint.y);
        Pair<Float, Float> movePolar = getPolarCoordinate(movePoint.x, movePoint.y);
        // 相对转角
        float angle = (movePolar.second - pressPolar.second);
        if (angle < 0) angle += 360; // 将角度值调整到0-360的范围
        if (angle > 180) angle -= 360; // 将角度值调整到-180-180的范围
        return angle;
    }

    private Pair<Float, Float> getPolarCoordinate(Float x, Float y) {
        double radius = sqrt(x * x + y * y);
        double angle = atan2(y, x);
        // 将角度值调整到正确的范围
        if (angle < 0) {
            angle += 2 * Math.PI;
        }
//        LogUtil.e(TAG, "getPolarCoordinate: " + radius + " " + toDegrees(angle));
        return new Pair<>((float) radius, (float) toDegrees(angle));
    }

    // 坐标相对中心点平移
    private PointF getRelativelyCoordinate(PointF point) {
        return new PointF(point.x - centerPoint.x, point.y - centerPoint.y);
    }
}
